From 0c0fe2fcc7ee6bfde47851287e53e1b8edab627f Mon Sep 17 00:00:00 2001 From: "emellor@ewan" Date: Mon, 19 Sep 2005 13:41:41 +0100 Subject: [PATCH] Use DevController.allocateDeviceID rather than using a local variable. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/XendDomain.py | 17 +- tools/python/xen/xend/XendDomainInfo.py | 575 ++++++++++++++---------- tools/python/xen/xend/image.py | 22 +- tools/python/xen/xend/server/netif.py | 10 +- 4 files changed, 345 insertions(+), 279 deletions(-) diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index e835ea9063..e85dac37a5 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -275,8 +275,7 @@ class XendDomain: @param config: configuration @return: domain """ - dominfo = XendDomainInfo.create(self.dbmap, config) - return dominfo + return XendDomainInfo.create(self.dbmap.getPath(), config) def domain_restart(self, dominfo): """Restart a domain. @@ -309,8 +308,7 @@ class XendDomain: @param vmconfig: vm configuration """ config = sxp.child_value(vmconfig, 'config') - dominfo = XendDomainInfo.restore(self.dbmap, config) - return dominfo + return XendDomainInfo.restore(self.dbmap.getPath(), config) def domain_restore(self, src, progress=False): """Restore a domain from file. @@ -352,13 +350,12 @@ class XendDomain: dompath = self.domroot log.info("Creating entry for unknown xend domain: id=%d uuid=%s", dom0, uuid) - db = self.dbmap.addChild("%s/xend" % uuid) try: - dominfo = XendDomainInfo.recreate(uuid, dompath, dom0, - db, info) - except: - raise XendError("Error recreating xend domain info: id=%d" % - dom0) + dominfo = XendDomainInfo.recreate(uuid, dompath, info) + except Exception, exn: + log.exception(exn) + raise XendError("Error recreating xend domain info: id=%d: %s" % + (dom0, str(exn))) self._add_domain(dominfo) return dominfo diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index f26323cb94..34014de31d 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -136,123 +136,179 @@ class XendDomainInfo: """ MINIMUM_RESTART_TIME = 20 - def create(cls, parentdb, config): + + def create(cls, parentdbpath, config): """Create a VM from a configuration. - @param parentdb: parent db + @param parentdbpath: parent db path @param config configuration @raise: VmError for invalid configuration """ - uuid = getUuid() - db = parentdb.addChild("%s/xend" % uuid) - path = parentdb.getPath() - vm = cls(uuid, path, db) - vm.construct(config) - vm.saveToDB(sync=True) + log.debug("XendDomainInfo.create(%s, ...)", parentdbpath) + + vm = cls(getUuid(), parentdbpath, cls.parseConfig(config)) + vm.construct() return vm create = classmethod(create) - def recreate(cls, uuid, path, domid, db, info): + + def recreate(cls, uuid, dompath, info): """Create the VM object for an existing domain. - @param db: domain db + @param dompath: The path to all domain information @param info: domain info from xc """ - vm = cls(uuid, path, db) - vm.setDomid(domid) - vm.name, vm.start_time = vm.gatherVm(("name", str), - ("start-time", float)) - try: - db.readDB() - except: pass - vm.importFromDB() - config = vm.config - log.debug('info=' + str(info)) - log.debug('config=' + prettyprintstring(config)) - vm.memory = info['mem_kb'] / 1024 - vm.target = info['mem_kb'] * 1024 + log.debug("XendDomainInfo.recreate(%s, %s, ...)", uuid, dompath) - if config: - try: - vm.recreate = True - vm.construct(config) - finally: - vm.recreate = False - else: - vm.setName("Domain-%d" % domid) + # ??? vm.setDomid(domid) +# vm.name, vm.start_time = vm.gatherVm(("name", str), +# ("start-time", float)) +# config = vm.config +# log.debug('info=' + str(info)) +# log.debug('config=' + prettyprintstring(config)) - vm.exportToDB(save=True) - return vm +# vm.memory = info['mem_kb'] / 1024 +# vm.target = info['mem_kb'] * 1024 + + + # Parse the configuration in the store, but allow the info we've + # been given (from xc) to override it. + path = "%s/%s" % (dompath, uuid) + + config = xstransact.Read(path, 'config') + if config: + temp_info = cls.parseConfig(config) + temp_info.update(info) + info = temp_info + + return cls(uuid, dompath, info) recreate = classmethod(recreate) - def restore(cls, parentdb, config, uuid=None): + + def restore(cls, parentdbpath, config, uuid = None): """Create a domain and a VM object to do a restore. - @param parentdb: parent db + @param parentdbpath: parent db @param config: domain configuration @param uuid: uuid to use """ + + log.debug("XendDomainInfo.restore(%s, ..., %s)", parentdbpath, uuid) + if not uuid: uuid = getUuid() - db = parentdb.addChild("%s/xend" % uuid) - path = parentdb.getPath() - vm = cls(uuid, path, db) - ssidref = int(sxp.child_value(config, 'ssidref')) - log.debug('restoring with ssidref='+str(ssidref)) - id = xc.domain_create(ssidref = ssidref) - vm.setDomid(id) - vm.clear_shutdown() + try: - vm.restore = True - vm.construct(config) - finally: - vm.restore = False - vm.exportToDB(save=True, sync=True) + ssidref = int(sxp.child_value(config, 'ssidref')) + except TypeError, exn: + raise VmError('Invalid ssidref in config: %s' % exn) + + log.debug('restoring with ssidref = %d' % ssidref) + + vm = cls(uuid, parentdbpath, cls.parseConfig(config), + xc.domain_create(ssidref = ssidref)) + vm.clear_shutdown() return vm restore = classmethod(restore) - __exports__ = [ - DBVar('config', ty='sxpr'), - DBVar('state', ty='str'), - DBVar('restart_mode', ty='str'), - DBVar('restart_state', ty='str'), - DBVar('restart_time', ty='float'), - DBVar('restart_count', ty='int'), - ] + + def parseConfig(cls, config): + def get_cfg(name, default = None): + return sxp.child_value(config, name, default) + + def int_get_cfg(name): + val = get_cfg(name) + if val: + return int(val) + else: + return None + + def flo_get_cfg(name): + val = get_cfg(name) + if val: + return float(val) + else: + return None + + + log.debug("parseConfig: config is %s" % str(config)) + + + result = {} + imagecfg = "()" + try: + result['name'] = get_cfg('name') + result['ssidref'] = int_get_cfg('ssidref') + result['memory'] = int_get_cfg('memory') + result['maxmem'] = int_get_cfg('maxmem') + result['cpu'] = int_get_cfg('cpu') + result['cpu_weight'] = flo_get_cfg('cpu_weight') + result['bootloader'] = get_cfg('bootloader') + result['restart_mode'] = get_cfg('restart') + + imagecfg = get_cfg('image') + + if imagecfg: + result['image'] = imagecfg + result['vcpus'] = int(sxp.child_value(imagecfg, 'vcpus', 1)) + else: + result['vcpus'] = 1 + + result['backend'] = [] + for c in sxp.children(config, 'backend'): + result['backend'].append(sxp.name(sxp.child0(c))) + + result['device'] = [] + for d in sxp.children(config, 'device'): + c = sxp.child0(d) + result['device'].append((sxp.name(c), c)) + + + log.debug("parseConfig: result is %s" % str(result)) + return result + + except TypeError, exn: + raise VmError( + 'Invalid ssidref / memory / cpu / cpu_weight / vcpus value: %s %s %s %s %s: %s' % + (get_cfg('ssidref'), + get_cfg('memory'), + get_cfg('cpu'), + get_cfg('cpu_weight'), + sxp.child_value(imagecfg, 'vcpus', 1), + str(exn))) + + parseConfig = classmethod(parseConfig) + - def __init__(self, uuid, path, db): + def __init__(self, uuid, parentpath, info, domid = None): + self.uuid = uuid - self.path = path + "/" + uuid + self.info = info - self.db = db + self.path = parentpath + "/" + uuid + + if domid: + self.domid = domid + elif 'dom' in info: + self.domid = int(info['dom']) + else: + self.domid = None + + self.validateInfo() - self.recreate = 0 - self.restore = 0 - - self.config = None - self.domid = None - self.cpu_weight = 1 self.start_time = None - self.name = None - self.memory = None - self.ssidref = None self.image = None - self.target = None - self.store_channel = None self.store_mfn = None self.console_channel = None self.console_mfn = None - self.info = None - self.backend_flags = 0 - #todo: state: running, suspended self.state = STATE_VM_OK self.state_updated = threading.Condition() @@ -261,17 +317,69 @@ class XendDomainInfo: #todo: set to migrate info if migrating self.migrate = None - self.restart_mode = RESTART_ONREBOOT self.restart_state = None self.restart_time = None self.restart_count = 0 - self.vcpus = 1 - self.bootloader = None + self.device_model_pid = 0 self.writeVm("uuid", self.uuid) self.storeDom("vm", self.path) + + def validateInfo(self): + """Validate and normalise the info block. This has either been parsed + by parseConfig, or received from xc through recreate. + """ + def defaultInfo(name, val): + if name not in self.info or self.info[name] is None: + self.info[name] = val() + + + try: + defaultInfo('name', lambda: "Domain-%d" % self.domid) + defaultInfo('memory', lambda: self.info['mem_kb'] / 1024) + defaultInfo('maxmem', lambda: self.info['memory']) + defaultInfo('restart_mode', lambda: RESTART_ONREBOOT) + defaultInfo('cpu_weight', lambda: 1.0) + defaultInfo('bootloader', lambda: None) + defaultInfo('backend', lambda: []) + defaultInfo('device', lambda: []) + + # vm.target = info['mem_kb'] * 1024 + + # !!! Consistency checks have the possibility of going wrong if + # xen updates us with a setting which should stay consistent with + # another. We should be storing only one value instead. + + # Validate the given backend names. + for s in self.info['backend']: + if s not in backendFlags: + raise VmError('Invalid backend type: %s' % s) + + for (n, c) in self.info['device']: + if not n or not c or n not in controllerClasses: + raise VmError('invalid device (%s, %s)' % + (str(n), str(c))) + + if self.info['restart_mode'] not in restart_modes: + raise VmError('invalid restart mode: ' + + str(self.info['restart_mode'])) + + if self.info['memory'] <= 0: + # !!! memory / mem_kb consistency check + raise VmError('Invalid memory size: %d', self.info['memory']) + + if 'cpumap' not in self.info: + if [self.info['vcpus'] == 1]: + self.info['cpumap'] = [1]; + else: + raise VmError('Cannot create CPU map') + + except KeyError, exn: + raise VmError('Unspecified domain detail: %s' % str(exn)) + + def readVm(self, *args): return xstransact.Read(self.path, *args) @@ -302,18 +410,27 @@ class XendDomainInfo: def storeDom(self, *args): return xstransact.Store(self.path, *args) - def setDB(self, db): - self.db = db - def saveToDB(self, save=False, sync=False): - self.db.saveDB(save=save, sync=sync) + def exportToDB(self, save=False): + to_store = { + 'id': str(self.domid), + 'uuid': self.uuid, + 'config': sxp.to_string(self.sxpr()), - def exportToDB(self, save=False, sync=False): - self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync) + 'start_time': str(self.start_time), + 'state': self.state, + 'restart_time': str(self.restart_time), + 'restart_count': str(self.restart_count), + 'device_model_pid': str(self.device_model_pid) + } + + for (k, v) in self.info.items(): + to_store[k] = str(v) + + log.debug("Storing %s" % str(to_store)) + + self.writeVm(to_store) - def importFromDB(self): - self.db.importFromDB(self, fields=self.__exports__) - self.store_channel = self.eventChannel("store/port") def setDomid(self, domid): """Set the domain id. @@ -327,11 +444,11 @@ class XendDomainInfo: return self.domid def setName(self, name): - self.name = name + self.info['name'] = name self.storeVm("name", name) def getName(self): - return self.name + return self.info['name'] def getPath(self): return self.path @@ -340,14 +457,14 @@ class XendDomainInfo: return self.uuid def getVCpuCount(self): - return self.vcpus + return self.info['vcpus'] def getSsidref(self): - return self.ssidref + return self.info['ssidref'] def getMemoryTarget(self): """Get this domain's target memory size, in MiB.""" - return self.memory + return self.info['memory'] def setStoreRef(self, ref): self.store_mfn = ref @@ -355,7 +472,8 @@ class XendDomainInfo: def getBackendFlags(self): - return self.backend_flags + return reduce(lambda x, y: x | backendFlags[y], + self.info['backend'], 0) def closeStoreChannel(self): @@ -379,18 +497,19 @@ class XendDomainInfo: def setMemoryTarget(self, target): self.storeDom("memory/target", target) - def update(self, info=None): - """Update with info from xc.domain_getinfo(). + + def update(self, info = None): + """Update with info from xc.domain_getinfo(). """ - if info: - self.info = info - else: - di = dom_get(self.domid) - if not di: + + if not info: + info = dom_get(self.domid) + if not info: return - self.info = di - self.memory = self.info['mem_kb'] / 1024 - self.ssidref = self.info['ssidref'] + + self.info.update(info) + self.validateInfo() + def state_set(self, state): self.state_updated.acquire() @@ -398,7 +517,7 @@ class XendDomainInfo: self.state = state self.state_updated.notifyAll() self.state_updated.release() - self.saveToDB() + self.exportToDB() def state_wait(self, state): self.state_updated.acquire() @@ -409,9 +528,9 @@ class XendDomainInfo: def __str__(self): s = " Created domain=%d name=%s memory=%d', + self.domid, self.info['name'], self.info['memory']) + self.register_domain() # Create domain devices. - self.configure_backends() - self.configure_restart() self.construct_image() self.configure() self.exportToDB(save=True) @@ -559,41 +711,12 @@ class XendDomainInfo: xd._add_domain(self) self.exportToDB(save=True) - def configure_cpus(self, config): - try: - self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1')) - except: - raise VmError('invalid cpu weight') - self.memory = int(sxp.child_value(config, 'memory')) - if self.memory is None: - raise VmError('missing memory size') - self.setMemoryTarget(self.memory * (1 << 20)) - self.ssidref = int(sxp.child_value(config, 'ssidref')) - cpu = sxp.child_value(config, 'cpu') - if self.recreate and self.domid and cpu is not None and int(cpu) >= 0: - xc.domain_pincpu(self.domid, 0, 1< Created domain=%d name=%s memory=%d', - id, self.name, self.memory) - self.setDomid(id) - def eventChannel(self, path=None): """Create an event channel to the domain. @@ -731,14 +837,8 @@ class XendDomainInfo: self.console_channel = self.eventChannel("console/port") def create_configured_devices(self): - devices = sxp.children(self.config, 'device') - for d in devices: - dev_config = sxp.child0(d) - if dev_config is None: - raise VmError('invalid device') - dev_type = sxp.name(dev_config) - - self.createDevice(dev_type, dev_config) + for (n, c) in self.info['device']: + self.createDevice(n, c) def create_devices(self): @@ -770,14 +870,6 @@ class XendDomainInfo: self.configureDevice(deviceClass, devid, dev_config) - def configure_restart(self): - """Configure the vm restart mode. - """ - r = sxp.child_value(self.config, 'restart', RESTART_ONREBOOT) - if r not in restart_modes: - raise VmError('invalid restart mode: ' + str(r)) - self.restart_mode = r; - def restart_needed(self, reason): """Determine if the vm needs to be restarted when shutdown for the given reason. @@ -785,11 +877,11 @@ class XendDomainInfo: @param reason: shutdown reason @return True if needs restart, False otherwise """ - if self.restart_mode == RESTART_NEVER: + if self.info['restart_mode'] == RESTART_NEVER: return False - if self.restart_mode == RESTART_ALWAYS: + if self.info['restart_mode'] == RESTART_ALWAYS: return True - if self.restart_mode == RESTART_ONREBOOT: + if self.info['restart_mode'] == RESTART_ONREBOOT: return reason == 'reboot' return False @@ -821,7 +913,7 @@ class XendDomainInfo: tdelta = tnow - self.restart_time if tdelta < self.MINIMUM_RESTART_TIME: self.restart_cancel() - msg = 'VM %s restarting too fast' % self.name + msg = 'VM %s restarting too fast' % self.info['name'] log.error(msg) raise VmError(msg) self.restart_time = tnow @@ -840,14 +932,13 @@ class XendDomainInfo: self.exportToDB() self.restart_state = STATE_RESTART_BOOTING self.configure_bootloader() - self.construct(self.config) - self.saveToDB() + self.construct() + self.exportToDB() finally: self.restart_state = None def configure_bootloader(self): - self.bootloader = sxp.child_value(self.config, "bootloader") - if not self.bootloader: + if not self.info['bootloader']: return # if we're restarting with a bootloader, we need to run it # FIXME: this assumes the disk is the first device and @@ -858,30 +949,13 @@ class XendDomainInfo: if dev: disk = sxp.child_value(dev, "uname") fn = blkdev_uname_to_file(disk) - blcfg = bootloader(self.bootloader, fn, 1, self.vcpus) + blcfg = bootloader(self.info['bootloader'], fn, 1, self.info['vcpus']) if blcfg is None: msg = "Had a bootloader specified, but can't find disk" log.error(msg) raise VmError(msg) self.config = sxp.merge(['vm', ['image', blcfg]], self.config) - def configure_backends(self): - """Set configuration flags if the vm is a backend for netif or blkif. - Configure the backends to use for vbd and vif if specified. - """ - for c in sxp.children(self.config, 'backend'): - v = sxp.child0(c) - name = sxp.name(v) - if name == 'blkif': - self.backend_flags |= SIF_BLK_BE_DOMAIN - elif name == 'netif': - self.backend_flags |= SIF_NET_BE_DOMAIN - elif name == 'usbif': - pass - elif name == 'tpmif': - self.backend_flags |= SIF_TPM_BE_DOMAIN - else: - raise VmError('invalid backend type:' + str(name)) def configure(self): """Configure a vm. @@ -899,19 +973,16 @@ class XendDomainInfo: """ return + def configure_maxmem(self): - try: - maxmem = int(sxp.child_value(self.config, 'maxmem', self.memory)) - xc.domain_setmaxmem(self.domid, maxmem_kb = maxmem * 1024) - except: - raise VmError("invalid maxmem: " + - sxp.child_value(self.config, 'maxmem')) + xc.domain_setmaxmem(self.domid, + maxmem_kb = self.info['maxmem'] * 1024) def vcpu_hotplug(self, vcpu, state): """Disable or enable VCPU in domain. """ - if vcpu > self.vcpus: + if vcpu > self.info['vcpus']: log.error("Invalid VCPU %d" % vcpu) return if int(state) == 0: @@ -1000,16 +1071,24 @@ implements the device control specific to that device-class.""" controllerClasses = {} -def addControllerClass(device_class, cls): +"""A map of backend names and the corresponding flag.""" +backendFlags = {} + + +def addControllerClass(device_class, backend_name, backend_flag, cls): """Register a subclass of DevController to handle the named device-class. + + @param backend_flag One of the SIF_XYZ_BE_DOMAIN constants, or None if + no flag is to be set. """ cls.deviceClass = device_class + backendFlags[backend_name] = backend_flag controllerClasses[device_class] = cls from xen.xend.server import blkif, netif, tpmif, pciif, usbif -addControllerClass('vbd', blkif.BlkifController) -addControllerClass('vif', netif.NetifController) -addControllerClass('vtpm', tpmif.TPMifController) -addControllerClass('pci', pciif.PciController) -addControllerClass('usb', usbif.UsbifController) +addControllerClass('vbd', 'blkif', SIF_BLK_BE_DOMAIN, blkif.BlkifController) +addControllerClass('vif', 'netif', SIF_NET_BE_DOMAIN, netif.NetifController) +addControllerClass('vtpm', 'tpmif', SIF_TPM_BE_DOMAIN, tpmif.TPMifController) +addControllerClass('pci', 'pciif', None, pciif.PciController) +addControllerClass('usb', 'usbif', None, usbif.UsbifController) diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py index 6d7f363ffd..17eae3e3d0 100644 --- a/tools/python/xen/xend/image.py +++ b/tools/python/xen/xend/image.py @@ -145,22 +145,21 @@ class ImageHandler: except OSError, ex: log.warning("error removing bootloader file '%s': %s", f, ex) - def initDomain(self, dom, memory, ssidref, cpu, cpu_weight): + def initDomain(self, dom, memory, ssidref, cpu, cpu_weight, bootloading): """Initial domain create. @return domain id """ mem_kb = self.getDomainMemory(memory) - if not self.vm.restore: - dom = xc.domain_create(dom = dom or 0, ssidref = ssidref) - # if bootloader, unlink here. But should go after buildDomain() ? - if self.vm.bootloader: - self.unlink(self.kernel) - self.unlink(self.ramdisk) - if dom <= 0: - raise VmError('Creating domain failed: name=%s' % - self.vm.getName()) + dom = xc.domain_create(dom = dom or 0, ssidref = ssidref) + # if bootloader, unlink here. But should go after buildDomain() ? + if bootloading: + self.unlink(self.kernel) + self.unlink(self.ramdisk) + if dom <= 0: + raise VmError('Creating domain failed: name=%s' % + self.vm.getName()) log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom) xc.domain_setcpuweight(dom, cpu_weight) xc.domain_setmaxmem(dom, mem_kb) @@ -184,9 +183,6 @@ class ImageHandler: def createDomain(self): """Build the domain boot image. """ - if self.vm.recreate or self.vm.restore: - return - # Set params and call buildDomain(). self.flags = self.vm.getBackendFlags() diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py index 2e5addb9b9..127529ad56 100755 --- a/tools/python/xen/xend/server/netif.py +++ b/tools/python/xen/xend/server/netif.py @@ -27,9 +27,6 @@ from xen.xend import sxp from xen.xend.server.DevController import DevController -next_devid = 1 - - class NetifController(DevController): """Network interface controller. Handles all network devices for a domain. """ @@ -41,8 +38,6 @@ class NetifController(DevController): def getDeviceDetails(self, config): """@see DevController.getDeviceDetails""" - global next_devid - from xen.xend import XendRoot xroot = XendRoot.instance() @@ -52,9 +47,6 @@ class NetifController(DevController): val.append(sxp.child0(ipaddr)) return val - devid = next_devid - next_devid += 1 - script = os.path.join(xroot.network_script_dir, sxp.child_value(config, 'script', xroot.get_vif_script())) @@ -63,6 +55,8 @@ class NetifController(DevController): mac = sxp.child_value(config, 'mac') ipaddr = _get_config_ipaddr(config) + devid = self.allocateDeviceID() + back = { 'script' : script, 'mac' : mac, 'bridge' : bridge, -- 2.30.2